package com.eric.springboot.aspect;

import com.alibaba.fastjson.JSON;
import com.eric.springboot.entity.ApiLog;
import com.eric.springboot.entity.UserLoginRequest;
import com.eric.springboot.service.ApiLogService;
import com.eric.springboot.utils.ExceptionUtil;
import com.eric.springboot.utils.RequestUtil;
import com.eric.springboot.utils.SecurityUtils;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

/**
 * @author Eric
 * @date 2021.2.5
 * 日志切面
 */
@Component
@Aspect
@Slf4j
public class ApiLogAspect {

    @Autowired
    private ApiLogService apiLogService;

    /**
     * 配置织入点，对所有的controller进行增强
     */
    @Pointcut("execution(* com.eric.springboot.controller..*(..))")
    public void apiLog() {

    }

    /**
     * 处理成功返回
     *
     * @param joinPoint
     * @return
     */
    @Around("apiLog()")
    public Object doAfter(ProceedingJoinPoint joinPoint) throws Throwable {
        //开始时间
        long start = System.currentTimeMillis();
        //获取当前请求对象
        ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        //获取request
        assert requestAttributes != null;
        HttpServletRequest request = requestAttributes.getRequest();
        //执行方法
        Object result = joinPoint.proceed();
        //获取切点的签名
        Signature signature = joinPoint.getSignature();
        MethodSignature methodSignature = (MethodSignature) signature;
        Method method = methodSignature.getMethod();
        //类+方法
        String classMethod = signature.getDeclaringTypeName() + "." + method.getName();
        ApiLog apiLog = new ApiLog();
        //如果方法加了注解就读取相应的操作详情
        if (method.isAnnotationPresent(ApiOperation.class)) {
            ApiOperation annotation = method.getAnnotation(ApiOperation.class);
            apiLog.setDescription(annotation.value());
        }
        String userName = "未知用户";
        Map<String, Object> paramsMap = getRequestParamsByJoinPoint(joinPoint);
        try {
            userName = SecurityUtils.getCurrentUserName();
        }catch (Exception e){
            if (paramsMap.size() > 0){
                UserLoginRequest userLoginRequest = (UserLoginRequest) paramsMap.get("userLoginRequest");
                if (userLoginRequest != null){
                    userName = userLoginRequest.getUserName();
                    userLoginRequest.setPassword(null);
                }
            }
        }
        //构建apiLog
        apiLog.setUserName(userName)
                .setIp(RequestUtil.getRequestIp(request))
                .setParams(JSON.toJSONString(paramsMap.toString()))
                .setBrowser(RequestUtil.getBrowser(request))
                .setCastTime(System.currentTimeMillis()-start)
                .setUrl(request.getRequestURL().toString())
                .setType(1)
                .setClassMethod(classMethod);
        //插入数据库
        log.info(apiLog.toString());
        apiLogService.insert(apiLog);
        return result;
    }


    /**
     * 出现异常时候通知
     *
     * @param joinPoint 切点
     * @param e         异常
     */
    @AfterThrowing(pointcut = "apiLog()", throwing = "e")
    public void doAfterThrow(JoinPoint joinPoint, Exception e) {
        //获取当前请求对象
        ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        //获取request
        assert requestAttributes != null;
        HttpServletRequest request = requestAttributes.getRequest();
        //获取切点的签名
        Signature signature = joinPoint.getSignature();
        MethodSignature methodSignature = (MethodSignature) signature;
        Method method = methodSignature.getMethod();
        //类+方法
        String classMethod = signature.getDeclaringTypeName() + "." + method.getName();
        ApiLog apiLog = new ApiLog();
        //如果方法加了注解就读取相应的操作详情
        if (method.isAnnotationPresent(ApiOperation.class)) {
            ApiOperation annotation = method.getAnnotation(ApiOperation.class);
            apiLog.setDescription(annotation.value());
        }
        //构建apiLog
        apiLog.setUserName(null)
                .setIp(RequestUtil.getRequestIp(request))
                .setParams(JSON.toJSONString(getRequestParamsByJoinPoint(joinPoint)))
                .setBrowser(RequestUtil.getBrowser(request))
                .setUrl(request.getRequestURL().toString())
                .setExceptionDetail(ExceptionUtil.getStackTrace(e))
                .setType(2)
                .setClassMethod(classMethod);
        //插入数据库
        apiLogService.insert(apiLog);
    }

    /**
     * 获取入参
     *
     * @param proceedingJoinPoint
     * @return
     */
    private Map<String, Object> getRequestParamsByProceedingJoinPoint(ProceedingJoinPoint proceedingJoinPoint) {
        Map<String, Object> requestParams = new HashMap<>();
        //参数名
        String[] paramNames = ((MethodSignature) proceedingJoinPoint.getSignature()).getParameterNames();
        //参数值
        Object[] paramValues = proceedingJoinPoint.getArgs();
        for (int i = 0; i < paramNames.length; i++) {
            if ("password".equals(paramNames[i])){
                continue;
            }
            Object value = paramValues[i];
            //如果是文件对象
            if (value instanceof MultipartFile) {
                MultipartFile file = (MultipartFile) value;
                //获取文件名
                value = file.getOriginalFilename();
            }
            assert value != null;
            if (!value.toString().contains("password")){
                requestParams.put(paramNames[i], value);
            }
        }
        return requestParams;
    }

    /**
     * 获取入参
     *
     * @param joinPoint
     * @return
     */
    private Map<String, Object> getRequestParamsByJoinPoint(JoinPoint joinPoint) {
        Map<String, Object> requestParams = new HashMap<>();
        //参数名
        String[] paramNames = ((MethodSignature) joinPoint.getSignature()).getParameterNames();
        //参数值
        Object[] paramValues = joinPoint.getArgs();
        for (int i = 0; i < paramNames.length; i++) {
            if ("password".equals(paramNames[i])){
                continue;
            }
            Object value = paramValues[i];
            //如果是request或者是response对象跳过
            if (value instanceof HttpServletRequest || value instanceof HttpServletResponse){
                continue;
            }
            //如果是文件对象
            if (value instanceof MultipartFile) {
                MultipartFile file = (MultipartFile) value;
                //获取文件名
                value = file.getOriginalFilename();
            }
            assert value != null;
            if (!value.toString().contains("password")){
                requestParams.put(paramNames[i], value);
            }
        }
        return requestParams;
    }
}

